# -*- coding: utf-8 -*-
# @Time    : 2024/6/25 14:59
# @Author  : yujiahao
# @File    : 12_pandas_merge_concat.py
# @description:Pandas中的合并和连接操作


# todo 1、merge合并操作

"""
    Pandas 提供的 merge() 函数能够进行高效的合并操作，这与 SQL 关系型数据库的 MERGE 用法非常相似。
    从字面意思上不难理解，merge 翻译为“合并”，指的是将两个 DataFrame 数据表按照指定的规则进行连接，
    最后拼接成一个新的 DataFrame 数据表。

    merge() 函数的格式如下：
    pd.merge(left, right, how='inner', on=None, left_on=None, right_on=None,
             left_index=False, right_index=False, sort=True, suffixes=('_x', '_y'), copy=True)

    参数说明如下：

    参数名称    说明
    ---------------------------------------------------------------------------
    left        两个不同的 DataFrame 对象之一。
    right       另一个不同的 DataFrame 对象。
    on          指定用于连接的键（即列标签的名字），该键必须同时存在于左右两个 DataFrame 中。
                如果没有指定，并且其他参数也未指定，那么将会以两个 DataFrame 的列名交集作为连接键。
    left_on     指定左侧 DataFrame 中作连接键的列名。
                该参数在左、右列标签名不相同，但表达的含义相同时非常有用。
    right_on    指定右侧 DataFrame 中作连接键的列名。
    left_index  布尔参数，默认为 False。如果为 True 则使用左侧 DataFrame 的行索引作为连接键。
                若 DataFrame 具有多层索引 (MultiIndex)，则层的数量必须与连接键的数量相等。
    right_index 布尔参数，默认为 False。如果为 True 则使用右侧 DataFrame 的行索引作为连接键。
    how         要执行的合并类型，从 {'left', 'right', 'outer', 'inner'} 中取值，默认为“inner”内连接。
    sort        布尔值参数，默认为 True。它会将合并后的数据进行排序；
                若设置为 False，则按照 how 给定的参数值进行排序。
    suffixes    字符串组成的元组。当左右 DataFrame 存在相同列名时，
                通过该参数可以在相同的列名后附加后缀名，默认为 ('_x', '_y')。
    copy        默认为 True，表示对数据进行复制。

    注意：
    Pandas 库的 merge() 支持各种内外连接，与其相似的还有 join() 函数（默认为左连接）。
"""
import pandas as pd


def pandas_merge():
    left = pd.DataFrame({
        'id': [1, 2, 3, 4],
        'Name': ['Smith', 'Maiki', 'Hunter', 'Hilen'],
        'subject_id': ['sub1', 'sub2', 'sub4', 'sub6']})
    right = pd.DataFrame({
        'id': [1, 2, 3, 4],
        'Name': ['William', 'Albert', 'Tony', 'Allen'],
        'subject_id': ['sub2', 'sub4', 'sub3', 'sub6']})

    print(left)

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    print(right)

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # todo 1.1、 在单个键上进行合并操作
    '''通过 on 参数指定一个连接键'''

    print(pd.merge(left, right, on='id'))

    # todo 1.2、在多个键上进行合并操作
    print(pd.merge(left, right, on=['id', 'subject_id']))

    # todo 1.3、使用how参数合并
    '''
    通过 how 参数可以确定 DataFrame 中要包含哪些键，如果在左表、右表都不存在的键，
    那么合并后该键对应的值为 NaN。为了便于大家学习，我们将 how 参数和与其等价的 SQL 语句做了总结：
    
    Merge 方法    等效 SQL          描述
    ---------------------------------------------------------------------------
    left          LEFT OUTER JOIN   使用左侧对象的 key
    right         RIGHT OUTER JOIN  使用右侧对象的 key
    outer         FULL OUTER JOIN   使用左右两侧所有 key 的并集
    inner         INNER JOIN        使用左右两侧 key 的交集  
    '''

    # 1) left join
    # 以left侧的subject_id为键
    print(pd.merge(left, right, on='subject_id', how="left"))

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # 2) right join

    print(pd.merge(left, right, on='subject_id', how="right"))

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # 3) outer join(并集)
    # 求出两个subject_id的并集，并作为键
    print(pd.merge(left, right, on='subject_id', how="outer"))

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # 4) inner join(交集)
    # 求出两个subject_id的交集，并将结果作为键
    print(pd.merge(left, right, on='subject_id', how="inner"))


# todo 2、Pandas concat连接操作


"""
    Pandas 通过 concat() 函数能够轻松地将 Series 与 DataFrame 对象组合在一起，函数的语法格式如下：
    pd.concat(objs, axis=0, join='outer', join_axes=None, ignore_index=False)
    
    参数说明如下所示：
    
    参数名称      说明
    ---------------------------------------------------------------------------
    objs          一个序列或者是 Series、DataFrame 对象。
    axis          表示在哪个轴方向上（行或者列）进行连接操作，默认 axis=0 表示行方向。
    join          指定连接方式，取值为 {"inner", "outer"}，默认为 outer 表示取并集，inner 代表取交集。
    ignore_index  布尔值参数，默认为 False，如果为 True，表示不在连接的轴上使用索引。
    join_axes     表示索引对象的列表。
"""


def pandas_concat():
    # todo 2.1、concat()

    a = pd.DataFrame({'A': ['A0', 'A1', 'A2', 'A3'],
                      'B': ['B0', 'B1', 'B2', 'B3'],
                      'C': ['C0', 'C1', 'C2', 'C3'],
                      'D': ['D0', 'D1', 'D2', 'D3']},
                     index=[0, 1, 2, 3])
    b = pd.DataFrame({'A': ['A4', 'A5', 'A6', 'A7'],
                      'B': ['B4', 'B5', 'B6', 'B7'],
                      'C': ['C4', 'C5', 'C6', 'C7'],
                      'D': ['D4', 'D5', 'D6', 'D7']},
                     index=[4, 5, 6, 7])

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # 连接a与b
    print(pd.concat([a, b]))

    # 把指定的键与 DataFrame 对象连接
    a = pd.DataFrame({'A': ['A0', 'A1', 'A2', 'A3'],
                      'B': ['B0', 'B1', 'B2', 'B3'],
                      'C': ['C0', 'C1', 'C2', 'C3'],
                      'D': ['D0', 'D1', 'D2', 'D3']},
                     index=[0, 1, 2, 3])
    b = pd.DataFrame({'A': ['A4', 'A5', 'A6', 'A7'],
                      'B': ['B4', 'B5', 'B6', 'B7'],
                      'C': ['C4', 'C5', 'C6', 'C7'],
                      'D': ['D1', 'D2', 'D5', 'D6']},
                     index=[2, 3, 4, 5])

    # 连接a与b,并给a，b连接一个指定的键
    print(pd.concat([a, b], keys=['x', 'y']))

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # 上述示中，可以看出行索引 index 存在重复使用的现象，如果想让输出的行索引遵循依次递增的规则，那么需要将 ignore_index 设置为 True。

    # 连接a与b,设置 ignore_index 等于 True
    print(pd.concat([a, b], keys=['x', 'y'], ignore_index=True))

    # todo 2.2、append() 新版本被禁用了，使用concat
    '''如果要连接 Series 和 DataFrame 对象，有一个最方便、快捷的方法，那就是 append() 方法。该方法沿着 axis=0 （行方向）进行操作。'''

    a = pd.DataFrame({'A': ['A0', 'A1', 'A2', 'A3'],
                      'B': ['B0', 'B1', 'B2', 'B3'],
                      'C': ['C0', 'C1', 'C2', 'C3'],
                      'D': ['D0', 'D1', 'D2', 'D3']},
                     index=[0, 1, 2, 3])
    b = pd.DataFrame({'A': ['A4', 'A5', 'A6', 'A7'],
                      'B': ['B4', 'B5', 'B6', 'B7'],
                      'C': ['C4', 'C5', 'C6', 'C7'],
                      'D': ['D1', 'D2', 'D5', 'D6']},
                     index=[4, 5, 6, 7])
    # 沿着 axis=0，使用 apppend()方法连接a与b
    print(a.append(b))


def main():
    # pandas_merge()
    pandas_concat()


if __name__ == '__main__':
    main()
